<?php
/**
 * User: zoudingyong <sheyingtg@qq.com>
 * Date: 2021/09/24
 */

namespace app\api\controller;

use think\Controller;
use think\Db;
use think\facade\Cache;
use think\Request;
use \Firebase\JWT\JWT;
use think\facade\Log;
use think\facade\Env;

class Base extends Controller
{
    protected $jwt_key = '**33$#*Z3SDS99023*&**&'; // jwtkey
    protected $api_key = 'bH5wG1fB3iF3cD6dX0fA'; // apikey
    protected $time = 3600 * 24; // 超时时间
    protected $request; // 用来处理参数
    protected $_data; // 过滤后符合要求的参数

    public $error = '';

    public function __construct(Request $request)
    {
        parent::__construct();

        $this->request = $request;
        $this->_data = $this->check_params($this->request->param(true));

        $this->check_timestamp($this->request->only(['timeStamp']));
        $this->check_nonceStr($this->request->only(['nonceStr']));
        $this->check_sign($this->request->param());

        //接口调试
        if (Env::get('APP_DEBUG')) {
            Log::write("Api请求:" . "接口地址:" . $this->request->url() . "参数:" . json_encode([$_REQUEST]));
        }

    }

    /**
     * api 数据返回
     * @param $code
     * @param string $msg
     * @param array $data
     */
    public function return_msg($code, $msg = '', $data = [])
    {
        header('Content-Type:application/json; charset=utf-8');
        $return_data['code'] = $code;
        $return_data['message'] = $msg;
        $return_data['data'] = $data;
        die(json_encode($return_data));
    }


    /**
     * 验证请求是否超时
     * @param  [array] $arr [包含时间戳的参数数组]
     * @param $arr
     */
    public function check_timestamp($arr)
    {
        if (!isset($arr['timeStamp']) || intval($arr['timeStamp']) <= 1) {
            $this->return_msg(1, '时间戳不正确!', time());
        }
        if (time() - intval($arr['timeStamp']) > $this->time) {
            if (Env::get('APP_DEBUG')) {
                $data['传递时间'] = date('Y-m-d H:i:s', $arr['timeStamp']);;
                $data['系统时间'] = date('Y-m-d H:i:s', time());
                $data['传递时间戳'] = $arr['timeStamp'];
                $data['系统时间戳'] = time();
                $this->return_msg(1, '请求超时', ['debug' => $data]);
            } else {
                $this->return_msg(1, '请求超时!', date('Y-m-d H:i:s', time()));

            }

        }
    }

    /**
     * 检测nonceStr
     * @param  [array] $arr [包含时间戳的参数数组]
     * @param $arr
     */
    public function check_nonceStr($arr)
    {
        if (!isset($arr['nonceStr']) || empty($arr['nonceStr'])) {
            $this->return_msg(1, 'nonceStr不能为空!', time());
        }

        $checkCount = Cache::store('redis')->handler()->LLEN('api:nonceStr:' . $arr['nonceStr']);
        if ($checkCount) {
            $this->return_msg(1, '请求已失效!', time());
        }
    }

    /**
     * 签名
     * @param $timeStamp //时间戳
     * @param $nonceStr //随机字符串
     * @return string 返回签名
     */
    public function getPaySign($timeStamp, $nonceStr)
    {
        $arr['timeStamp'] = $timeStamp;
        $arr['nonceStr'] = $nonceStr;
        $arr['apiKey'] = $this->api_key;
        ksort($arr);
        $buff = '';
        foreach ($arr as $k => $v) $buff .= "{$k}={$v}";
        $sign = md5($buff);
        return $sign;
    }

    /**
     * 产生随机字符串
     * @param int $length 指定字符长度
     * @param string $str 字符串前缀
     * @return string
     */
    public function createNoncestr($length = 32, $str = "")
    {
        $chars = "abcdefghijklmnopqrstuvwxyz0123456789";
        for ($i = 0; $i < $length; $i++) {
            $str .= substr($chars, mt_rand(0, strlen($chars) - 1), 1);
        }
        return $str;
    }

    /**
     * 验证sign(防止篡改数据)
     * @param  [array] $arr [全部请求参数]
     * @return void [json]      [token验证结果]
     */
    public function check_sign($arr)
    {
        if (!isset($arr['sign']) || empty($arr['sign'])) {
            $this->return_msg(1, 'sign不能为空!');
        }
        $timeStamp = $arr['timeStamp'];
        $nonceStr = $arr['nonceStr'];
        $sign = $arr['sign'];
        unset($arr['sign']);
        $service_sign = $this->getPaySign($timeStamp, $nonceStr);  //后台计算的签名值

        if ($sign !== $service_sign) {
            if (Env::get('APP_DEBUG')) {
                $data['tip'] = '签名值sign计算方式：把timeStamp、nonceStr、apiKey数组元素按照键名进行升序排序，按照“参数=参数值”的模式拼接成字符串,并对字符串MD5得到sign值。timeStamp计算方式：APP启动后通过接口获取服务器时间 ServerTime，保存本地。并同时记录当前时间 LocalTime1 。timeStamp等于：LocalTime2（当前本地时间） - LocalTime1 + ServerTime';
                $data['timeStamp'] = $timeStamp;
                $data['nonceStr'] = $nonceStr;
                $data['apiKey'] = $this->api_key;
                $data['sign'] = $service_sign;
                $data['char'] = 'utf8';
                $data['currentTime'] = time();
                $data['url'] = request()->root(true) . '/api/' . $controller = $this->request->controller() . '/' . $this->request->action() . '?' . http_build_query($arr) . '&sign=' . $service_sign;

                $this->return_msg(1, '签名错误', ['debug' => $data]);
            } else {
                $this->return_msg(1, '签名错误[100]!');

            }
        }

        Cache::store('redis')->handler()->LPUSH('api:nonceStr:' . $nonceStr, 1); //队列
        Cache::store('redis')->handler()->expire('api:nonceStr:' . $nonceStr, $this->time);//设置过期时间
    }


    /** 检测用户token
     * @return bool
     */
    public function check_user_token()
    {

        if (!isset($this->_data['token']) || empty($this->_data['token'])) {
            $this->return_msg(2, '请先登陆!');
        }
        $token = $this->_data['token'];

        if ($token) {
            try {
                $obj_token = JWT::decode($token, $this->jwt_key, array('HS256'));
                $arr = json_decode(json_encode($obj_token), true);
                $member_id = $arr['member_id'];
                $mobile = Db::name('ai_member')->find($member_id);
                if ($mobile) {
                    return $mobile;
                } else {
                    $this->return_msg(1, '用户不存在');
                }
            } catch (\Exception $e) {
//                    $this->return_msg(1, 'token错误');
                $this->return_msg(1, $e->getMessage());
            }

        } else {
            $this->return_msg(2, '请登陆');
        }
    }


    /**
     * 检测参数
     * @param $arr
     * @return mixed
     */
    public function check_params($arr)
    {
        /*********** 获取参数的验证规则  ***********/
        $controller = substr(strrchr($this->request->controller(), "."), 1); //截取.后的控制器
        $controller = $this->request->controller();
        $rule = $this->request->action();
        /*********** 验证参数并返回错误  ***********/
        $hasScene = validate($controller)->hasScene($rule);
        if ($hasScene) {
            $validate = validate($controller)->scene($rule);
            if (!$validate->check($arr)) {
                $this->return_msg(1, $validate->getError());
            }
        }

        /*********** 如果正常,通过验证  ***********/
        return $arr;
    }

    /**
     * 返回操作成功json
     * @param string $msg
     * @param array $data
     * @return array
     */
    protected function renderSuccess($msg = 'success', $data = [])
    {
        return json(['code' => 0, 'message' => $msg, 'data' => $data]);
    }

    /**
     * 返回操作失败json
     * @param string $msg
     * @param array $data
     * @return array
     */
    protected function renderError($msg = 'error', $data = [])
    {
        return json(['code' => 1, 'message' => $msg, 'data' => $data]);
    }

    /**
     * 设置错误信息
     * @param $error
     */
    protected function setError($error)
    {
        empty($this->error) && $this->error = $error;
    }

    /**
     * 获取错误信息
     * @return string
     */
    protected function getError()
    {
        return $this->error;
    }

    /**
     * 是否存在错误
     * @return bool
     */
    protected function hasError()
    {
        return !empty($this->error);
    }

}